package com.rw.tool.util.expression.state;

import com.rw.tool.util.expression.element.Calculation;
import com.rw.tool.util.expression.element.ExpressionElement;
import com.rw.tool.util.expression.element.LeftParentheses;
import com.rw.tool.util.expression.element.NumberElement;
import com.rw.tool.util.expression.element.RightParentheses;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * 解析表达式状态
 */
public enum InfixExpressionState {
    /**
     * 表达式开始时，只能是数字和左括号
     */
    START(new Class[]{NumberElement.class, LeftParentheses.class}),

    /**
     * 运算符号之后，只能跟上数字和左括号
     */
    AFTER_CALCULATION(new Class[]{NumberElement.class, LeftParentheses.class}),

    /**
     * 数字之后，只能跟上运算符号和右括号
     */
    AFTER_NUMBER(new Class[]{Calculation.class, RightParentheses.class}),

    /**
     * 左括号之后，只能跟上数字和左括号
     */
    AFTER_LEFT_PARENTHESES(new Class[]{NumberElement.class, LeftParentheses.class}),

    /**
     * 右括号之后，只能跟上运算符号和右括号
     */
    AFTER_RIGHT_PARENTHESES(new Class[]{Calculation.class, RightParentheses.class});

    private final Set<Class<? extends ExpressionElement>> canBeFollowed;

    InfixExpressionState(Class<? extends ExpressionElement>[] element) {
        canBeFollowed = new HashSet<>(Arrays.asList(element));
    }

    /**
     * 根据元素获取当前状态
     */
    public static InfixExpressionState of(ExpressionElement element) {
        Class<? extends ExpressionElement> elementClass = element.getClass();
        if (NumberElement.class.isAssignableFrom(elementClass)) {
            return AFTER_NUMBER;
        } else if (Calculation.class.isAssignableFrom(elementClass)) {
            return AFTER_CALCULATION;
        } else if (LeftParentheses.class.isAssignableFrom(elementClass)) {
            return AFTER_LEFT_PARENTHESES;
        } else if (RightParentheses.class.isAssignableFrom(elementClass)) {
            return AFTER_RIGHT_PARENTHESES;
        }
        throw new IllegalArgumentException("Unknown element! : " + elementClass);
    }

    /**
     * 获取能够跟随的表达式元素
     */
    public Set<Class<? extends ExpressionElement>> getCanBeFollowed() {
        return Collections.unmodifiableSet(canBeFollowed);
    }

    /**
     * 判断当前状态是否能够跟随指定表达式元素
     */
    public boolean canBeFollowed(ExpressionElement element) {
        return canBeFollowed.stream().anyMatch(target->target.isAssignableFrom(element.getClass()));
    }
}
